package edu.erpan.bl.bean;

import java.math.BigDecimal;
import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.persistence.Query;

import edu.erpan.bl.dto.ConsumoMateriaPrima;
import edu.erpan.bl.entities.Cliente;
import edu.erpan.bl.entities.LineaPedido;
import edu.erpan.bl.entities.LineaPedidoPK;
import edu.erpan.bl.entities.MateriaPrima;
import edu.erpan.bl.entities.Pedido;
import edu.erpan.util.entityManager.GenericEntityManager;
import edu.erpan.util.entityManager.exceptions.IdNotFoundException;
import edu.erpan.util.entityManager.exceptions.PersistException;
import edu.erpan.util.entityManager.exceptions.QueryException;

/**
 * Session Bean implementation class OrdenPedidoBean
 */
@Stateful
@LocalBean
public class OrdenPedidoBean extends GenericEntityManager<Pedido> implements OrdenPedidoBeanRemote {
       
	public OrdenPedidoBean() {
		super(Pedido.class);
	}
	
	public OrdenPedidoBean(Class<Pedido> clase) {
		super(clase);
	}

	@Override
	public Pedido consultarPedido(Pedido pedido) throws QueryException,
			IdNotFoundException {
		try{
			pedido = findEntity(pedido);
		}catch (Exception e) {
			throw new QueryException(e);
		}
		return pedido;
	}

	@SuppressWarnings("unchecked")
	private List<Pedido> consultarPedidos(int estado) throws QueryException {
		String queryString= "SELECT pedido FROM "+Pedido.class.getName()+" pedido WHERE pedido.estado = :estado ORDER BY pedido.fechaRegistro ASC";
		Query query=getEntityManager().createQuery(queryString);
		List<Pedido>pedidos=null;
		try{
			query.setParameter("estado", estado);
			pedidos=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}
		return pedidos;
	}
	
	@Override
	public List<Pedido> consultarPedidosAProduccion() throws QueryException {
		return consultarPedidos(Pedido.ESTADO_A_PRODUCCION);
	}
	
	@Override
	public List<Pedido> consultarPedidosPendientes() throws QueryException {
		return consultarPedidos(Pedido.ESTADO_PENDIENTE);
	}
	
	@Override
	public List<Pedido> consultarPedidosCancelados() throws QueryException {
		return consultarPedidos(Pedido.ESTADO_CANCELADO);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Pedido guardarPedido(Pedido pedido, List<LineaPedido> lineasPedido)
			throws PersistException {
		try{
			saveEntity(pedido);
			
			String consultarUltimoPedido="SELECT pedido FROM "+Pedido.class.getName()+" pedido " +
					"ORDER BY pedido.idpedido DESC";
			Query query=this.getEntityManager().createQuery(consultarUltimoPedido);
			query.setMaxResults(1);
			
			List<Pedido>resultado=query.getResultList();
			if(!resultado.isEmpty()){
				pedido=resultado.get(0);
			}
			
			for(LineaPedido lineaPedido:lineasPedido){
				LineaPedidoPK id=new LineaPedidoPK();
				id.setPedidoIdpedido(pedido.getIdpedido());
				id.setProductoIdproducto(lineaPedido.getProducto().getIdproducto());
				
				lineaPedido.setId(id);
				
				this.getEntityManager().merge(lineaPedido);
			}
			reducirExistenciasEnInventarioDePedido(pedido,lineasPedido);
			saveEntity(pedido);
		}catch (Exception e) {
			throw new PersistException(e);
		}
		return pedido;
	}

	protected void reducirExistenciasEnInventarioDePedido(Pedido pedido,
			List<LineaPedido> lineasPedido) throws QueryException, PersistException {
		List<ConsumoMateriaPrima>consumos=calcularConsumoDeMateriaPrima(pedido);
		boolean hayExistencias=true;
		for (ConsumoMateriaPrima consumoMateriaPrima : consumos) {
			if(consumoMateriaPrima.getDiferenciaConsumo().compareTo(BigDecimal.ZERO)==-1){
				hayExistencias=false;
				break;
			}
		}
		
		if(!hayExistencias){
			pedido.setEstado(Pedido.ESTADO_PENDIENTE);
			return;
		}
		
		for (ConsumoMateriaPrima consumoMateriaPrima : consumos) {
			MateriaPrima materiaPrima=consumoMateriaPrima.getMateriaPrima();
			BigDecimal inventario=materiaPrima.getInventario();
			
			
			if(inventario==null){
				inventario=BigDecimal.ZERO;
			}
			inventario=inventario.subtract(consumoMateriaPrima.getCantidadFormula());
			materiaPrima.setInventario(inventario);
			
			this.getEntityManager().merge(materiaPrima);
		}
	}
	
	protected void reestablecerExistenciasEnInventarioDePedido(Pedido pedido,
			List<LineaPedido> lineasPedido) throws QueryException, PersistException {
		List<ConsumoMateriaPrima>consumos=calcularConsumoDeMateriaPrima(pedido);
		
		for (ConsumoMateriaPrima consumoMateriaPrima : consumos) {
			MateriaPrima materiaPrima=consumoMateriaPrima.getMateriaPrima();
			BigDecimal inventario=materiaPrima.getInventario();
			
			
			if(inventario==null){
				inventario=BigDecimal.ZERO;
			}
			inventario=inventario.add(consumoMateriaPrima.getCantidadFormula());
			materiaPrima.setInventario(inventario);
			
			this.getEntityManager().merge(materiaPrima);
		}
	}

	@Override
	public void editarOrdenPedido(Pedido pedido) throws PersistException {
		try{
			mergeEntity(pedido);
			
			if(pedido.getEstado()==Pedido.ESTADO_A_PRODUCCION){
				List<LineaPedido>lineasPedido=consultarLineasPedido(pedido);
				
				reestablecerExistenciasEnInventarioDePedido(pedido, lineasPedido);
				reducirExistenciasEnInventarioDePedido(pedido, lineasPedido);
				saveEntity(pedido);
			}
		}catch (Exception e) {
			throw new PersistException(e);
		}
	}

	@Override
	public void eliminarPedido(Pedido pedido) throws QueryException {
		try{
			for(LineaPedido lineaPedido:pedido.getLineasPedido()){
				lineaPedido=this.getEntityManager().getReference(LineaPedido.class, lineaPedido.getId());
				this.getEntityManager().remove(lineaPedido);
			}
			deleteEntity(pedido);
		}catch (Exception e) {
			throw new QueryException(e);
		}		
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<LineaPedido> consultarLineasPedido(Pedido pedido)
			throws QueryException {
		List<LineaPedido>lineasPedido=null;
		try{
			StringBuffer stringQuery=new StringBuffer("SELECT lineaPedido FROM "+LineaPedido.class.getName()+" lineaPedido ");
			stringQuery.append("INNER JOIN lineaPedido.pedido pedido ");
			stringQuery.append("WHERE pedido.idpedido = :idPedido ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			query.setParameter("idPedido", pedido.getIdpedido());
			
			lineasPedido=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return lineasPedido;
	}
	
	/**
	 * consulta la informacion de materia prima necesaria para que un pedido pueda entrar a produccion
	 * 
	 * @param pedido
	 * @return
	 * @throws QueryException
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<ConsumoMateriaPrima> calcularConsumoDeMateriaPrima(Pedido pedido) throws QueryException{
		List<ConsumoMateriaPrima>lineasPedido=null;
		try{
			StringBuffer stringQuery=new StringBuffer("select new ")
				.append(ConsumoMateriaPrima.class.getCanonicalName()).append("(mp,sum(lp.cantidad*f.cantidad),(sum(mp.inventario)-sum(lp.cantidad*f.cantidad))) ")
				.append("from ").append(Pedido.class.getCanonicalName()).append(" ped ")
				.append("inner join ped.lineasPedido lp ") 
				.append("inner join lp.producto pro ")
				.append("inner join pro.formulaciones f ") 
				.append("inner join f.materiaPrima mp ")
				.append("where ped.idpedido = :idPedido ")
				.append("group by ")
				.append("mp.idmateriaPrima,mp.nombre ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			query.setParameter("idPedido", pedido.getIdpedido());
			
			lineasPedido=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return lineasPedido;
	}
	
	/**
	 * consulta la informacion de materia prima necesaria para que un pedido pueda entrar a produccion
	 * 
	 * @param pedido
	 * @return
	 * @throws QueryException
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<ConsumoMateriaPrima> calcularConsumoDeMateriaPrimaTodosLosPedidos(int estado) throws QueryException{
		List<ConsumoMateriaPrima>lineasPedido=null;
		try{
			StringBuffer stringQuery=new StringBuffer("select new ")
				.append(ConsumoMateriaPrima.class.getCanonicalName()).append("(mp,sum(f.cantidad),(sum(mp.inventario)-sum(f.cantidad))) ")
				.append("from ").append(Pedido.class.getCanonicalName()).append(" ped ")
				.append("inner join ped.lineasPedido lp ") 
				.append("inner join lp.producto pro ")
				.append("inner join pro.formulaciones f ") 
				.append("inner join f.materiaPrima mp ")
				.append("where ped.estado = :estadoPedido ")
				.append("group by ")
				.append("mp.idmateriaPrima,mp.nombre ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			query.setParameter("estadoPedido", estado);
			
			lineasPedido=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return lineasPedido;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Cliente> consultarClientes() throws QueryException {
		List<Cliente>clientes=null;
		try{
			StringBuffer stringQuery=new StringBuffer("select cliente ")
				.append("from ").append(Cliente.class.getCanonicalName()).append(" cliente ")
				.append("order by cliente.nombre asc ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			clientes=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return clientes;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Cliente> consultarClientesActivos() throws QueryException {
		List<Cliente>clientes=null;
		try{
			StringBuffer stringQuery=new StringBuffer("select cliente ")
				.append("from ").append(Cliente.class.getCanonicalName()).append(" cliente ")
				.append("where ")
				.append("cliente.activo = :activo ")
				.append("order by cliente.nombre asc ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			query.setParameter("activo", true);
			clientes=query.getResultList();
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return clientes;
	}
	
	@Override
	public void cancelarOrdenPeddido(Pedido pedido) throws PersistException {
		try{
			mergeEntity(pedido);
			
			List<LineaPedido>lineasPedido=consultarLineasPedido(pedido);
			
			reestablecerExistenciasEnInventarioDePedido(pedido, lineasPedido);
			saveEntity(pedido);
		}catch (Exception e) {
			throw new PersistException(e);
		}
	}

	@Override
	public void pasarOrdenPedidoAProduccion(Pedido pedido) throws PersistException {
		try{
			saveEntity(pedido);
			
			List<LineaPedido>lineasPedido=consultarLineasPedido(pedido);
			
			for(LineaPedido lineaPedido:lineasPedido){
				LineaPedidoPK id=new LineaPedidoPK();
				id.setPedidoIdpedido(pedido.getIdpedido());
				id.setProductoIdproducto(lineaPedido.getProducto().getIdproducto());
				
				lineaPedido.setId(id);
				
				this.getEntityManager().merge(lineaPedido);
			}
			reducirExistenciasEnInventarioDePedido(pedido,lineasPedido);
			saveEntity(pedido);
		}catch (Exception e) {
			throw new PersistException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Cliente guardarCliente(Cliente cliente) throws PersistException {
		try{
			getEntityManager().merge(cliente);
			
			String consultarUltimoCliente="SELECT cliente FROM "+Cliente.class.getName()+" cliente " +
					"ORDER BY cliente.idcliente DESC";
			Query query=this.getEntityManager().createQuery(consultarUltimoCliente);
			query.setMaxResults(1);
			
			List<Cliente>resultado=query.getResultList();
			if(!resultado.isEmpty()){
				cliente=resultado.get(0);
			}
		}catch (Exception e) {
			throw new PersistException(e);
		}
		return cliente;
	}

	@Override
	public void editarCliente(Cliente cliente) throws PersistException {
		try{
			getEntityManager().merge(cliente);
		}catch (Exception e) {
			throw new PersistException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Cliente consultarClientesPorIdentificacion(Cliente cliente)
			throws QueryException {
		List<Cliente>clientes=null;
		try{
			StringBuffer stringQuery=new StringBuffer("select cliente ")
				.append("from ").append(Cliente.class.getCanonicalName()).append(" cliente ")
				.append("where ")
				.append("cliente.numeroIdentificacion = :numeroIdentificacion ")
				.append("and cliente.tipoIdentificacion.idtipoIdentificacion = :idtipoId ")
				.append(cliente.getIdcliente()>0?"and cliente.idcliente <> :idcliente ":"")
				.append("order by cliente.nombre asc ");
			
			Query query=this.getEntityManager().createQuery(stringQuery.toString());
			query.setParameter("numeroIdentificacion", cliente.getNumeroIdentificacion());
			query.setParameter("idtipoId", cliente.getTipoIdentificacion().getIdtipoIdentificacion());
			if(cliente.getIdcliente()>0){
				query.setParameter("idcliente", cliente.getIdcliente());
			}
			clientes=query.getResultList();
			if(clientes.size()>0){
				cliente=clientes.get(0);
			}else{
				return null;
			}
		}catch (Exception e) {
			throw new QueryException(e);
		}	
		return cliente;
	}

	@Override
	public Cliente consultarCliente(Cliente cliente) throws QueryException {
		StringBuffer stringBuffer=new StringBuffer("SELECT cliente FROM "+Cliente.class.getName()+" cliente ");
		stringBuffer.append("WHERE cliente.idcliente = :idCliente ");
		
		String queryString=stringBuffer.toString();
		Query query=getEntityManager().createQuery(queryString);
		query.setParameter("idCliente", cliente.getIdcliente());
		
		Cliente clienteRS=null;
		try{
			clienteRS=(Cliente) query.getSingleResult();
		}catch (Exception e) {
			throw new QueryException(e);
		}
		return clienteRS;
	}
}
